﻿namespace NetTopologySuite.IO.EsriJSON
{
    public class EsriJsonWriter
    {
        public virtual string Write(Geometry geometry)
        {
            var JSON = new EsriJSON();

            if (geometry.SRID > 0)
            {
                JSON.spatialReference = new SpatialReference(geometry.SRID);
            }

            if (geometry.IsEmpty)
            {
                return JSON.ToEsriJson();
            }
            else if (geometry.OgcGeometryType == OgcGeometryType.Point)
            {
                var frtCdt = geometry.Coordinate;
                JSON.x = frtCdt.X;
                JSON.y = frtCdt.Y;
                if (!double.IsNaN(frtCdt.Z) && !double.IsNaN(frtCdt.M))
                {
                    JSON.hasZ = true;
                    JSON.hasM = true;
                    JSON.z = frtCdt.Z;
                    JSON.m = frtCdt.M;
                }
                else if (!double.IsNaN(frtCdt.Z))
                {
                    JSON.hasZ = true;
                    JSON.z = frtCdt.Z;
                }
                else if (!double.IsNaN(frtCdt.M))
                {
                    JSON.hasM = true;
                    JSON.m = frtCdt.M;
                }
                return JSON.ToEsriJson();
            }
            else
            {
                var frtCdt = geometry.Coordinate;
                if (!double.IsNaN(frtCdt.Z) && !double.IsNaN(frtCdt.M))
                {
                    JSON.hasZ = true;
                    JSON.hasM = true;
                }
                else if (!double.IsNaN(frtCdt.Z))
                {
                    JSON.hasZ = true;
                }
                else if (!double.IsNaN(frtCdt.M))
                {
                    JSON.hasM = true;
                }

                if (geometry.OgcGeometryType == OgcGeometryType.MultiPoint)
                {
                    var cdts = geometry.Coordinates;
                    if (cdts.Length > 0)
                    {
                        JSON.points = new List<List<double>>();
                        foreach (var cdt in cdts)
                        {
                            JSON.points.Add(GetItem(cdt, JSON));
                        }
                    }
                }
                else if (geometry.OgcGeometryType == OgcGeometryType.LineString || geometry.OgcGeometryType == OgcGeometryType.MultiLineString)
                {
                    var lstLineString = new List<LineString>();
                    if (geometry.OgcGeometryType == OgcGeometryType.LineString)
                    {
                        lstLineString.Add((geometry as LineString));
                    }
                    else
                    {
                        lstLineString = (geometry as MultiLineString).Geometries.Select(v => v as LineString).ToList();
                    }

                    if (lstLineString.Count > 0)
                    {
                        JSON.paths = new List<List<List<double>>>();
                        foreach (var lineString in lstLineString)
                        {
                            var lstLine = new List<List<double>>();
                            foreach (var cdt in lineString.Coordinates)
                            {
                                lstLine.Add(GetItem(cdt, JSON));
                            }
                            JSON.paths.Add(lstLine);
                        }
                    }
                }
                else if (geometry.OgcGeometryType == OgcGeometryType.Polygon || geometry.OgcGeometryType == OgcGeometryType.MultiPolygon)
                {
                    var lstPolygon = new List<Polygon>();
                    if (geometry.OgcGeometryType == OgcGeometryType.Polygon)
                    {
                        lstPolygon.Add((geometry as Polygon));
                    }
                    else
                    {
                        lstPolygon = (geometry as MultiPolygon).Geometries.Select(v => v as Polygon).ToList();
                    }

                    if (lstPolygon.Count > 0)
                    {
                        JSON.rings = new List<List<List<double>>>();
                        foreach (var lineString in lstPolygon)
                        {
                            var lstLine = new List<List<double>>();
                            foreach (var cdt in lineString.Coordinates)
                            {
                                lstLine.Add(GetItem(cdt, JSON));
                            }
                            JSON.rings.Add(lstLine);
                        }
                    }
                }
            }
            return JSON.ToEsriJson();
        }

        private List<double> GetItem(Coordinate cdt, EsriJSON esri)
        {
            var point = new List<double>()
                {
                    cdt.X,
                    cdt.Y
                };
            if (esri.hasZ)
            {
                point.Add(cdt.Z);
            }
            if (esri.hasM)
            {
                point.Add(cdt.M);
            }
            return point;
        }


    }
}
